#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
平台检测器
自动检测项目平台类型并配置相应的过滤规则
"""

import os
import re
from copy import deepcopy
from typing import Dict, List, Set, Tuple
from pathlib import Path

from ..utils_module.language_utils import collect_extensions
from ..builtin_config_module.builtin_config_define import BuiltinConfigDefine

class PlatformDetector:
    """平台检测器 - 自动识别iOS/Android项目"""

    def __init__(self):
        """
        初始化平台检测器

        设置iOS、Android和混合框架的特征指标，用于自动识别项目类型。
        支持原生iOS/Android项目以及React Native、Flutter等混合框架。
        """
        # iOS项目特征文件
        self.ios_indicators = {
            'files': [
                '*.xcodeproj',
                '*.xcworkspace',
                'Podfile',
                'Cartfile',
                'Package.swift',
                'Info.plist',
                'Assets.xcassets',
                '*.storyboard',
                '*.xib'
            ],
            'dirs': [
                'Pods',
                'Carthage',
                'Frameworks'
            ],
            'extensions': list(collect_extensions(['swift', 'objc']))
        }

        # Android项目特征文件
        self.android_indicators = {
            'files': [
                'build.gradle',
                'build.gradle.kts',
                'settings.gradle',
                'settings.gradle.kts',
                'gradle.properties',
                'gradlew',
                'gradlew.bat',
                'AndroidManifest.xml',
                'proguard-rules.pro',
                'local.properties'
            ],
            'dirs': [
                'gradle',
                '.gradle',
                'app',
                'src/main/java',
                'src/main/kotlin',
                'src/main/res'
            ],
            'extensions': list(collect_extensions(['kotlin', 'java']))
        }

        # 混合框架特征（React Native, Flutter, Ionic等）
        self.hybrid_frameworks = {
            'react-native': {
                'files': ['package.json', 'metro.config.js', 'app.json', 'index.js', 'App.js', 'App.tsx'],
                'dirs': ['node_modules', 'android', 'ios'],
                'package_indicators': ['react-native', '@react-native'],
                'weight': 10  # 高权重，优先识别
            },
            'flutter': {
                'files': ['pubspec.yaml', 'pubspec.lock', 'analysis_options.yaml'],
                'dirs': ['.dart_tool', 'android', 'ios', 'lib', 'test'],
                'extensions': ['.dart'],
                'weight': 10
            },
            'ionic': {
                'files': ['ionic.config.json', 'capacitor.config.json', 'capacitor.config.ts'],
                'dirs': ['www', 'src', 'android', 'ios'],
                'package_indicators': ['@ionic', '@capacitor'],
                'weight': 10
            },
            'cordova': {
                'files': ['config.xml', '.cordova'],
                'dirs': ['platforms', 'plugins', 'www'],
                'weight': 10
            },
            'xamarin': {
                'files': ['*.sln', '*.csproj'],
                'dirs': ['Droid', 'iOS', 'Android', 'Shared'],
                'extensions': ['.cs', '.xaml'],
                'weight': 10
            },
            'nativescript': {
                'files': ['nativescript.config.ts', 'nsconfig.json'],
                'dirs': ['app', 'platforms'],
                'package_indicators': ['@nativescript'],
                'weight': 10
            }
        }
        
    def detect_platform(self, project_path: str) -> str:
        """
        自动检测项目平台类型

        支持：
        - 原生 iOS/Android
        - 混合框架 (React Native, Flutter, Ionic, Cordova, Xamarin)
        - 准确识别混合项目，避免误判

        返回: 'ios', 'android', 'mixed', 'react-native', 'flutter', 等
        """
        # 首先检测混合框架（优先级最高）
        framework = self._detect_hybrid_framework(project_path)
        if framework:
            return framework

        ios_score = 0
        android_score = 0
        files_scanned = 0

        # 检查特征文件（仅扫描前两层目录以提高性能）
        max_depth = 2
        # 确保 project_path 是字符串
        project_path_str = str(project_path)
        # 要跳过的目录列表（提高性能）
        skip_dirs = {'node_modules', '.git', 'build', 'dist', '.gradle', 'DerivedData', 'Pods'}

        for root, dirs, files in os.walk(project_path_str):
            # 计算当前深度
            depth = root[len(project_path_str):].count(os.sep)
            if depth > max_depth:
                # 清空dirs以停止进一步递归
                dirs[:] = []
                continue

            # 从遍历列表中移除需要跳过的目录（修改dirs列表会影响os.walk的遍历）
            dirs[:] = [d for d in dirs if d not in skip_dirs]

            files_scanned += len(files)

            # iOS特征检测
            for pattern in self.ios_indicators['files']:
                # 检查文件
                if any(self._match_pattern(f, pattern) for f in files):
                    ios_score += 2
                # 某些特征是目录（如 *.xcodeproj, *.xcworkspace）
                elif any(self._match_pattern(d, pattern) for d in dirs):
                    ios_score += 2

            for dir_name in self.ios_indicators['dirs']:
                if dir_name in dirs:
                    ios_score += 2

            # Android特征检测
            for pattern in self.android_indicators['files']:
                if any(self._match_pattern(f, pattern) for f in files):
                    android_score += 2

            for dir_name in self.android_indicators['dirs']:
                if dir_name in dirs or dir_name in str(root):
                    android_score += 2

            # 统计文件扩展名
            for file in files:
                ext = os.path.splitext(file)[1]
                if ext in self.ios_indicators['extensions']:
                    ios_score += 1
                if ext in self.android_indicators['extensions']:
                    android_score += 1

        # 保存统计信息
        self._files_scanned = files_scanned
        self._platform_scores = {
            'ios': ios_score,
            'android': android_score
        }

        # 判断平台（调整阈值以更好识别混合项目）
        if ios_score == 0 and android_score == 0:
            return 'unknown'
        elif ios_score >= 4 and android_score >= 4:
            # 同时具有iOS和Android强特征，且非框架项目
            return 'mixed'
        elif ios_score > android_score * 2:
            return 'ios'
        elif android_score > ios_score * 2:
            return 'android'
        elif ios_score > android_score:
            return 'ios'
        elif android_score > ios_score:
            return 'android'
        elif ios_score > 0:
            return 'ios'
        else:
            return 'unknown'

    def _detect_hybrid_framework(self, project_path: str) -> str:
        """
        检测混合框架类型

        返回: 框架名称或None
        """
        # 如果路径不存在，返回None
        if not os.path.exists(project_path) or not os.path.isdir(project_path):
            return None

        for framework_name, indicators in self.hybrid_frameworks.items():
            score = 0
            feature_count = 0  # 特征计数（文件+目录+依赖）

            # 检查特征文件
            if 'files' in indicators:
                for file_pattern in indicators['files']:
                    # 支持通配符（搜索根目录和子目录）
                    if '*' in file_pattern:
                        import glob
                        # 搜索根目录
                        matches = glob.glob(os.path.join(project_path, file_pattern))
                        # 搜索一级子目录
                        matches += glob.glob(os.path.join(project_path, '*', file_pattern))
                        if matches:
                            score += 2
                            feature_count += 1
                    else:
                        file_path = os.path.join(project_path, file_pattern)
                        if os.path.exists(file_path):
                            score += 2
                            feature_count += 1

            # 检查特征目录（也算作特征）
            if 'dirs' in indicators:
                for dir_name in indicators['dirs']:
                    dir_path = os.path.join(project_path, dir_name)
                    if os.path.isdir(dir_path):
                        score += 1
                        feature_count += 0.5  # 目录权重较低

            # 检查package.json中的依赖（React Native, Ionic等）
            if 'package_indicators' in indicators:
                package_json = os.path.join(project_path, 'package.json')
                if os.path.exists(package_json):
                    try:
                        import json
                        with open(package_json, 'r', encoding='utf-8') as f:
                            package_data = json.load(f)

                        dependencies = package_data.get('dependencies', {})
                        dev_dependencies = package_data.get('devDependencies', {})
                        all_deps = {**dependencies, **dev_dependencies}

                        for indicator in indicators['package_indicators']:
                            # 检查依赖名是否包含指标
                            if any(indicator in dep for dep in all_deps.keys()):
                                score += 3
                                feature_count += 1
                    except (json.JSONDecodeError, IOError, KeyError) as e:
                        # 忽略package.json解析错误，继续检测
                        pass

            # 检查特征扩展名（在根目录或lib/app等常见目录）
            if 'extensions' in indicators:
                try:
                    has_extension = False
                    # 检查根目录
                    for file in os.listdir(project_path):
                        if any(file.endswith(ext) for ext in indicators['extensions']):
                            has_extension = True
                            break

                    # 检查常见子目录（lib, app, src）
                    if not has_extension:
                        for subdir in ['lib', 'app', 'src', 'test']:
                            subdir_path = os.path.join(project_path, subdir)
                            if os.path.isdir(subdir_path):
                                for file in os.listdir(subdir_path):
                                    if any(file.endswith(ext) for ext in indicators['extensions']):
                                        has_extension = True
                                        break
                                if has_extension:
                                    break

                    if has_extension:
                        score += 1
                        feature_count += 0.5
                except (OSError, PermissionError):
                    pass

            # 判断是否为该框架
            # 策略：需要足够的特征匹配（放宽条件）
            if feature_count >= 2 and score >= 4:
                return framework_name
            elif feature_count >= 3:  # 或者有3+个特征
                return framework_name

        return None
    
    def _match_pattern(self, filename: str, pattern: str) -> bool:
        """匹配文件名模式"""
        if '*' in pattern:
            import fnmatch
            return fnmatch.fnmatch(filename, pattern)
        return filename == pattern
    
    def detect_third_party_libs(self, project_path: str, platform: str) -> Set[str]:
        """检测项目中使用的第三方库"""
        detected_libs = set()

        ios_like = {
            'ios', 'mixed', 'react-native', 'flutter',
            'ionic', 'cordova', 'xamarin', 'nativescript', 'unified'
        }
        android_like = {
            'android', 'mixed', 'react-native', 'flutter',
            'ionic', 'cordova', 'xamarin', 'nativescript', 'unified'
        }

        if platform in ios_like:
            # 检查Podfile
            podfile_path = os.path.join(project_path, 'Podfile')
            if os.path.exists(podfile_path):
                with open(podfile_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    for lib in BuiltinConfigDefine.get_third_party_libs('ios'):
                        if lib.lower() in content.lower():
                            detected_libs.add(lib)

            # 检查Cartfile
            cartfile_path = os.path.join(project_path, 'Cartfile')
            if os.path.exists(cartfile_path):
                with open(cartfile_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    for lib in BuiltinConfigDefine.get_third_party_libs('ios'):
                        if lib.lower() in content.lower():
                            detected_libs.add(lib)

        if platform in android_like:
            # 检查build.gradle文件
            for root, dirs, files in os.walk(project_path):
                for file in files:
                    if file in ['build.gradle', 'build.gradle.kts']:
                        file_path = os.path.join(root, file)
                        with open(file_path, 'r', encoding='utf-8') as f:
                            content = f.read()
                            for lib in BuiltinConfigDefine.get_third_party_libs('android'):
                                if lib.lower() in content.lower():
                                    detected_libs.add(lib)

        return detected_libs
    
    def get_proguard_rules(self, project_path: str) -> List[str]:
        """获取ProGuard规则"""
        rules = []
        proguard_files = [
            'proguard-rules.pro',
            'proguard-android.txt',
            'proguard-android-optimize.txt',
            'consumer-rules.pro'
        ]
        
        for root, dirs, files in os.walk(project_path):
            for file in files:
                if file in proguard_files:
                    file_path = os.path.join(root, file)
                    with open(file_path, 'r', encoding='utf-8') as f:
                        rules.extend(f.readlines())
        
        return rules
    
    def extract_keep_rules(self, proguard_rules: List[str]) -> Dict[str, List[str]]:
        """提取ProGuard的keep规则"""
        keep_classes = []
        keep_methods = []
        keep_fields = []
        
        for rule in proguard_rules:
            rule = rule.strip()
            if rule.startswith('#') or not rule:
                continue
            
            # 提取keep规则
            if '-keep' in rule:
                # 匹配类
                class_match = re.search(r'class\s+([\w.*]+)', rule)
                if class_match:
                    keep_classes.append(class_match.group(1))
                
                # 匹配方法
                method_match = re.search(r'(\w+)\s*\([^)]*\)', rule)
                if method_match:
                    keep_methods.append(method_match.group(1))
                
                # 匹配字段
                field_match = re.search(r'(\w+)\s*;', rule)
                if field_match:
                    keep_fields.append(field_match.group(1))
        
        return {
            'classes': keep_classes,
            'methods': keep_methods,
            'fields': keep_fields
        }
    
    def generate_platform_config(self, project_path: str) -> Dict:
        """生成平台特定的配置"""
        platform = self.detect_platform(project_path)
        third_party_libs = self.detect_third_party_libs(project_path, platform)
        
        config = {
            'platform': platform,
            'detected_libraries': list(third_party_libs),
            'recommended_settings': {}
        }

        ios_settings = {
            'obfuscation_config': {
                'swift': {'enabled': True},
                'objc': {'enabled': True},
                'kotlin': {'enabled': False},
                'java': {'enabled': False}
            },
            'exclude_prefixes': ['UI', 'NS', 'CG', 'CA', 'CF', 'AV', 'WK'],
            'exclude_dirs': ['Pods', 'Carthage', '.git', 'build', 'DerivedData']
        }

        android_settings = {
            'obfuscation_config': {
                'swift': {'enabled': False},
                'objc': {'enabled': False},
                'kotlin': {'enabled': True},
                'java': {'enabled': True}
            },
            'exclude_prefixes': ['android', 'androidx', 'com.google', 'java', 'javax', 'kotlin'],
            'exclude_dirs': ['.gradle', 'gradle', 'build', '.idea', 'node_modules']
        }

        hybrid_settings = {
            'obfuscation_config': {
                'swift': {'enabled': True},
                'objc': {'enabled': True},
                'kotlin': {'enabled': True},
                'java': {'enabled': True}
            },
            'exclude_prefixes': [
                'UI', 'NS', 'CG', 'CA', 'CF', 'AV', 'WK',
                'android', 'androidx', 'com.google', 'java', 'javax', 'kotlin'
            ],
            'exclude_dirs': [
                'Pods', 'Carthage', '.git', 'build', 'DerivedData',
                '.gradle', 'gradle', '.idea', 'node_modules'
            ]
        }

        if platform == 'ios':
            config['recommended_settings'] = deepcopy(ios_settings)
        elif platform == 'android':
            config['recommended_settings'] = deepcopy(android_settings)

            # 添加ProGuard规则
            proguard_rules = self.get_proguard_rules(project_path)
            if proguard_rules:
                keep_rules = self.extract_keep_rules(proguard_rules)
                config['proguard_keep_rules'] = keep_rules
        elif platform in {'mixed', 'unified'}:
            config['recommended_settings'] = deepcopy(hybrid_settings)
        elif platform == 'react-native':
            rn_settings = deepcopy(hybrid_settings)
            rn_settings['exclude_dirs'].extend([
                'node_modules', 'android/.gradle', 'android/.idea',
                'ios/Pods', 'ios/DerivedData'
            ])
            rn_settings['exclude_dirs'] = list(dict.fromkeys(rn_settings['exclude_dirs']))
            config['recommended_settings'] = rn_settings
            config['notes'] = "React Native 项目通常包含 iOS 与 Android 宿主代码，推荐分别处理原生目录并忽略 JS/TS 代码。"
        elif platform == 'flutter':
            flutter_settings = deepcopy(hybrid_settings)
            flutter_settings['exclude_dirs'].extend([
                '.dart_tool', 'lib', 'test'
            ])
            flutter_settings['exclude_dirs'] = list(dict.fromkeys(flutter_settings['exclude_dirs']))
            config['recommended_settings'] = flutter_settings
            config['notes'] = "Flutter 项目主要逻辑在 Dart 中，建议仅处理 ios/ 与 android/ 宿主代码。"
        elif platform in {'ionic', 'cordova', 'nativescript'}:
            hybrid_web_settings = deepcopy(hybrid_settings)
            hybrid_web_settings['exclude_dirs'].extend(['www', 'dist', 'build'])
            hybrid_web_settings['exclude_dirs'] = list(dict.fromkeys(hybrid_web_settings['exclude_dirs']))
            config['recommended_settings'] = hybrid_web_settings
            config['notes'] = "该类型项目以 Web 资源为主，可仅对原生插件代码执行混淆。"
        elif platform == 'xamarin':
            xamarin_settings = deepcopy(hybrid_settings)
            xamarin_settings['obfuscation_config'] = {
                'swift': {'enabled': False},
                'objc': {'enabled': False},
                'kotlin': {'enabled': False},
                'java': {'enabled': False}
            }
            config['recommended_settings'] = xamarin_settings
            config['notes'] = "Xamarin 以 C# 为主，当前混淆器不直接支持，请结合外部 .NET 工具链。"

        # 添加第三方库特定的排除规则
        if third_party_libs:
            config['third_party_exclusions'] = self._get_library_exclusions(third_party_libs, platform)
        
        return config
    
    def _get_library_exclusions(self, libraries: Set[str], platform: str) -> Dict[str, List[str]]:
        """获取第三方库的排除规则"""
        exclusions = {
            'classes': [],
            'methods': [],
            'prefixes': []
        }

        # 使用BuiltinConfigDefine中定义的库排除规则
        for lib in libraries:
            rules = BuiltinConfigDefine.get_library_exclusion_rules(lib)
            if rules:
                if 'prefixes' in rules:
                    exclusions['prefixes'].extend(rules['prefixes'])
                if 'classes' in rules:
                    exclusions['classes'].extend(rules['classes'])
                if 'methods' in rules:
                    exclusions['methods'].extend(rules['methods'])

        # 去重
        for key in exclusions:
            exclusions[key] = list(set(exclusions[key]))

        return exclusions

    def get_statistics(self) -> Dict[str, any]:
        """
        获取平台检测统计信息

        返回统计字典，包含：
        - total_files_scanned: 扫描的文件总数
        - platform_scores: 各平台的得分
        """
        return {
            'total_files_scanned': getattr(self, '_files_scanned', 0),
            'platform_scores': getattr(self, '_platform_scores', {})
        }
